package io.github.rascaler.dcenter.db;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.util.IOUtils;
import io.github.rascaler.dcenter.pojo.Candlestick;
import io.github.rascaler.dcenter.pojo.CandlestickRank;
import io.github.rascaler.dcenter.pojo.FixedArrayList;
import io.github.rascaler.dcenter.pojo.Instrument;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;

import java.io.*;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.*;
import java.util.zip.DeflaterOutputStream;
import java.util.zip.GZIPInputStream;
import java.util.zip.GZIPOutputStream;
import java.util.zip.InflaterInputStream;

import com.alibaba.fastjson.TypeReference;
import org.apache.commons.lang3.StringUtils;

/**
 * @ClassName MemoryDB
 * @Description TODO
 * @Author wurenqing
 * @Date 2023-11-24 14:11
 */

@Slf4j
@Data
public class MemoryDB {
    public MemoryDB() {}

    public void init() {
        log.error("初始化");
//        String[] symbols = new String[]{"1000FLOKIUSDT","1000LUNCUSDT","1000PEPEUSDT","1000SHIBUSDT","1000XECUSDT","1INCHUSDT","AAVEUSDT","ACHUSDT","ADAUSDT","AGIXUSDT","AGLDUSDT","ALGOUSDT","ALICEUSDT","ALPHAUSDT","AMBUSDT","ANKRUSDT","ANTUSDT","APEUSDT","API3USDT","APTUSDT","ARUSDT","ARBUSDT","ARKUSDT","ARKMUSDT","ARPAUSDT","ASTRUSDT","ATAUSDT","ATOMUSDT","AUDIOUSDT","AVAXUSDT","AXSUSDT","BADGERUSDT","BAKEUSDT","BALUSDT","BANDUSDT","BATUSDT","BCHUSDT","BEAMXUSDT","BELUSDT","BICOUSDT","BIGTIMEUSDT","BLUEBIRDUSDT","BLURUSDT","BLZUSDT","BNBUSDT","BNTUSDT","BNXUSDT","BONDUSDT","BSVUSDT","BTCUSDT","BTCDOMUSDT","C98USDT","CAKEUSDT",
//                "CELOUSDT","CELRUSDT","CFXUSDT","CHRUSDT","CHZUSDT","CKBUSDT","COCOSUSDT","COMBOUSDT","COMPUSDT","COTIUSDT","CRVUSDT","CTKUSDT","CTSIUSDT","CVXUSDT","CYBERUSDT","DARUSDT","DASHUSDT","DEFIUSDT","DENTUSDT","DGBUSDT","DODOXUSDT","DOGEUSDT","DOTUSDT","DUSKUSDT","DYDXUSDT","EDUUSDT","EGLDUSDT","ENJUSDT","ENSUSDT","EOSUSDT","ETCUSDT","ETHUSDT","FETUSDT","FILUSDT","FLMUSDT","FLOWUSDT","FOOTBALLUSDT","FRONTUSDT","FTMUSDT","FXSUSDT","GALUSDT","GALAUSDT","GASUSDT","GLMRUSDT","GMTUSDT","GMXUSDT","GRTUSDT","GTCUSDT","HBARUSDT","HFTUSDT","HIFIUSDT","HIGHUSDT","HOOKUSDT","HOTUSDT","ICPUSDT","ICXUSDT","IDUSDT","IDEXUSDT","ILVUSDT","IMXUSDT","INJUSDT","IOSTUSDT","IOTAUSDT","IOTXUSDT","JASMYUSDT",
//                "JOEUSDT","KASUSDT","KAVAUSDT","KEYUSDT","KLAYUSDT","KNCUSDT","KSMUSDT","LDOUSDT","LEVERUSDT","LINAUSDT","LINKUSDT","LITUSDT","LOOMUSDT","LPTUSDT","LQTYUSDT","LRCUSDT","LTCUSDT","LUNA2USDT","MAGICUSDT","MANAUSDT","MASKUSDT","MATICUSDT","MAVUSDT","MBLUSDT","MDTUSDT","MEMEUSDT","MINAUSDT","MKRUSDT","MTLUSDT","NEARUSDT","NEOUSDT","NKNUSDT","NMRUSDT","NTRNUSDT","OCEANUSDT","OGNUSDT","OMGUSDT","ONEUSDT","ONTUSDT","OPUSDT","ORBSUSDT","ORDIUSDT","OXTUSDT","PENDLEUSDT","PEOPLEUSDT","PERPUSDT","PHBUSDT","POLYXUSDT","POWRUSDT","QNTUSDT","QTUMUSDT","RADUSDT","RDNTUSDT","REEFUSDT","RENUSDT","RIFUSDT","RLCUSDT","RNDRUSDT","ROSEUSDT","RSRUSDT","RUNEUSDT","RVNUSDT","SANDUSDT","SEIUSDT","SFPUSDT",
//                "SKLUSDT","SLPUSDT","SNTUSDT","SNXUSDT","SOLUSDT","SPELLUSDT","SSVUSDT","STEEMUSDT","STGUSDT","STMXUSDT","STORJUSDT","STPTUSDT","STRAXUSDT","STXUSDT","SUIUSDT","SUSHIUSDT","SXPUSDT","TUSDT","THETAUSDT","TIAUSDT","TLMUSDT","TOKENUSDT","TOMOUSDT","TRBUSDT","TRUUSDT","TRXUSDT","TWTUSDT","UMAUSDT","UNFIUSDT","UNIUSDT","USDCUSDT","VETUSDT","WAVESUSDT","WAXPUSDT","WLDUSDT","WOOUSDT","XEMUSDT","XLMUSDT","XMRUSDT","XRPUSDT","XTZUSDT","XVGUSDT","XVSUSDT","YFIUSDT","YGGUSDT","ZECUSDT","ZENUSDT","ZILUSDT","ZRXUSDT",
//        };
////        String[] symbols = new String[]{"BTCUSDT"};
//        Arrays.stream(symbols).forEach(symbol -> {
//            Instrument instrument = new Instrument();
//            instrument.setSymbol(symbol);
//            instruments.add(instrument);
//        });
        loadDatas();
        thread = new Thread() {
            @Override
            public void run() {
                while (true) {
                    try {
                        log.error("保存数据任务开始执行");
                        Thread.sleep(saveConfig.timeInterval);
                        saveDatas();
                        log.error("保存数据任务结束");
                    } catch (InterruptedException e) {
                        log.error("保存数据任务执行失败", e);
                    }
                }
            }
        };
        thread.start();
    }

    public MemoryDB(SaveConfig config) {
        this.saveConfig = config;
    }

    private SaveConfig saveConfig;

    private Thread thread;

    private Map<String,Instrument> instrumentMap = new HashMap<>();

    private Map<String, FixedArrayList<Candlestick>> barMap = new HashMap<>();

    private Map<Long, List<CandlestickRank>> rankMap = new HashMap<>();

    public FixedArrayList<Candlestick> getBars(String key) {
        FixedArrayList<Candlestick> bars = barMap.get(key);
        if (null == bars) {
            bars = new FixedArrayList<>(FixedArrayList.MAX_SIZE);
            barMap.put(key, bars);
        }
        return bars;
    }

    public Map<String, FixedArrayList<Candlestick>> getBarMap() {
        return barMap;
    }

//    public void loadDatas() {
////        try {
////            long start = System.currentTimeMillis();
////            log.error("加载instrument数据......");
////            // 将 JSON 字符串转换为 JSON 对象
////            String path = saveConfig.getPath() + "/instruments.json";
////            if (!Files.exists(Paths.get(path))) {
////                log.error("{}不存在", path);
////                return;
////            }
////            String jsonContent = decompressFromFile(path);
////            instrumentMap = JSON.parseObject(jsonContent, new TypeReference<Map<String, Instrument>>() {});
////            if (null == instrumentMap) {
////                instrumentMap = new HashMap<>();
////            }
////            log.error("instrument数据加载完成，耗时{}......", System.currentTimeMillis() - start);
////        } catch (IOException e) {
////            log.error("加载instrument数据失败", e);
////        }
//
//        try {
//            long start = System.currentTimeMillis();
//            log.error("加载数据......");
//            // 将 JSON 字符串转换为 JSON 对象
//            String path = saveConfig.getPath() + "/bars.json";
//            if (!Files.exists(Paths.get(path))) {
//                log.error("{}不存在", path);
//                return;
//            }
//            String jsonContent = decompressFromFile(path);
//            barMap = JSON.parseObject(jsonContent, new TypeReference<Map<String, FixedArrayList<Candlestick>>>() {});
//            if (null == barMap) {
//                barMap = new HashMap<>();
//            }
//            log.error("bar数据加载完成，耗时{}......", System.currentTimeMillis() - start);
//        } catch (IOException e) {
//            log.error("加载bar数据失败", e);
//        }
//    }

    public void loadDatas() {

            long start = System.currentTimeMillis();
            log.error("加载数据......");
            // 将 JSON 字符串转换为 JSON 对象
            String path = saveConfig.getPath() + "/bars.csv";
            if (!Files.exists(Paths.get(path))) {
                log.error("{}不存在", path);
                return;
            }
        try(FileInputStream inputStream = new FileInputStream(path);
            BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream));) {
            String line = null;
            while(StringUtils.isNotBlank(line = bufferedReader.readLine()))
            {
                Object[] candlestick = Candlestick.parseCsv(line, ";");
                String key = candlestick[0].toString();
                FixedArrayList<Candlestick> bars = barMap.get(key);
                if (null == bars) {
                    bars = new FixedArrayList<>(FixedArrayList.MAX_SIZE);
                    barMap.put(key, bars);
                }
                bars.add((Candlestick) candlestick[1]);
            }
            log.error("数据加载完成，耗时{}......", System.currentTimeMillis() - start);
        } catch (IOException e) {
            log.error("加载数据失败", e);
        }
    }

//    public void saveDatas() {
//        // save instruments
////        try(FileOutputStream outputStream = new FileOutputStream(saveConfig.getPath() + "/instruments.json");
////            DeflaterOutputStream deflaterOutput = new DeflaterOutputStream(outputStream);) {
////            // 将 JSON 对象转换为 JSON 字符串
////            byte[] jsonContent = JSON.toJSONBytes(instrumentMap);
////            deflaterOutput.write(jsonContent);
////        } catch (IOException e) {
////            log.error("保存instrument数据失败", e);
////        }
//
//        // save bars
//        try(FileOutputStream outputStream = new FileOutputStream(saveConfig.getPath() + "/bars.json");
//            DeflaterOutputStream deflaterOutput = new DeflaterOutputStream(outputStream);) {
//            // 将 JSON 对象转换为 JSON 字符串
//             byte[] jsonContent = JSON.toJSONBytes(barMap);
//            deflaterOutput.write(jsonContent);
//        } catch (IOException e) {
//            log.error("保存bar数据失败", e);
//        }
//    }

    public void saveDatas() {
        try(FileOutputStream outputStream = new FileOutputStream(saveConfig.getPath() + "/bars.csv");
            BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(outputStream);) {
            // 防止遍历过程中写入新的数据导致遍历失败
            Map<String, FixedArrayList<Candlestick>> cloneMap = new HashMap<>(barMap);
            for (Map.Entry entry: barMap.entrySet()) {
                String key = (String) entry.getKey();
                // 防止遍历过程中写入新的数据导致遍历失败
                FixedArrayList<Candlestick> value = new FixedArrayList<>((FixedArrayList<Candlestick>) entry.getValue());
                for (Candlestick v: value) {
                    bufferedOutputStream.write(v.toCsv(key, ";").getBytes());
                }
            }
        } catch (IOException e) {
            log.error("保存数据失败", e);
        }
    }



    public static String decompressFromFile(String fileName) throws IOException {
        // 从文件中读取压缩后的数据
        byte[] compressedData = Files.readAllBytes(Paths.get(fileName));

        // 解压缩数据
        ByteArrayInputStream inputStream = new ByteArrayInputStream(compressedData);
        InflaterInputStream inflaterInput = new InflaterInputStream(inputStream);

        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
        byte[] buffer = new byte[1024];
        int length;

        while ((length = inflaterInput.read(buffer)) > 0) {
            outputStream.write(buffer, 0, length);
        }

        return new String(outputStream.toByteArray(), StandardCharsets.UTF_8);
    }


    @NoArgsConstructor
    @AllArgsConstructor
    @Data
    public static class SaveConfig {

        // 定时保存快照时间，单位：秒
        private Integer timeInterval;

        // 快照保存目录
        private String path;
    }

}
